/**
 * @file XYXKC_IOT.cpp
 * @brief XYXKC's ESPONE Mind+ library.
 * @n this is the iot module of xiaoyingxing
 *
 * @copyright    XYXKC,2024
 * @copyright    MIT Lesser General Public License
 *
 * @author [email](hyc@xyxedu.cn)
 * @date  2024-8-27
 */
#include "XYXKC_IOT.h"
volatile static bool mqttCallbackBusy;
volatile static bool isTheTaskExists;
volatile static bool isWifiTheTaskExists;
volatile unsigned long mqttTime;
volatile unsigned long wifiTime;
WiFiClient espClient;
PubSubClient client(espClient);
XYXKC_IOT *mqttIot;
XYXKC_IOT::XYXKC_IOT(void) {
        isTheTaskExists = false;
        isWifiTheTaskExists = false;
        mqttCallbackBusy = false;
        mqttTime = 0;
        wifiTime = 0;
        mqttIot=this;

        
}
XYXKC_IOT::~XYXKC_IOT(void) {
        isTheTaskExists = false;
        isWifiTheTaskExists = false;
        mqttCallbackBusy = false;
        mqttIot=NULL;
}
void XYXKC_IOT::setReconnectWIFI(bool reconnect) {
    autoReconnectWiFi = reconnect;
    // WiFi.setAutoReconnect(reconnect);
}

static void takeWifiConnected(void* parameter) {

    while (true) {
        if(!mqttIot->isWiFiConnected()){
            mqttIot->reconnectWIFI();
            vTaskDelay(100 / portTICK_PERIOD_MS);
        }


        vTaskDelay(50 / portTICK_PERIOD_MS);
    }
}

void XYXKC_IOT::WiFiConnection( String ssid,  String password) {
    this->ssid = ssid;
    this->password = password;
    WiFi.begin(this->ssid.c_str(), this->password.c_str());
    if(!isWifiTheTaskExists){
        isWifiTheTaskExists = true;
        if(this->autoReconnectWiFi){
            xTaskCreatePinnedToCore(
                    takeWifiConnected,
                    "Task2",
                    2048,
                    this,
                    1,
                    NULL,
                    XPORTGETCOREID
            );
        }
    }


}

void XYXKC_IOT::WiFiHotSpot(String ssid,  String password) {
    WiFi.softAP(ssid.c_str(), password.c_str());
}


static void mqttCallback(char * topic, byte * payload, unsigned int len){
    mqttCallbackBusy = true;
    String _topic = topic;
    for(int i = 0; i < mqttIot->mqtt_customTopicArray.size(); i++){
        if(_topic == mqttIot->mqtt_customTopicArray[i]){
            String _payload = (char *)payload;
            _payload = _payload.substring(0,len);
            for(int j = 0; j < mqttIot->mqtt_customHandleCb[i].size(); j++){
                if(_payload == mqttIot->mqtt_customDelimiters[i][j]){
                    mqttIot->mqtt_customHandleCb[i][j]();
                    break;
                }
            }
            if(mqttIot->mqtt_customMsgHandleCb[i])
                mqttIot->mqtt_customMsgHandleCb[i](_payload);
            break;
        }
    }
    delay(20);
    yield();
    mqttCallbackBusy = false;
}
void XYXKC_IOT::setReconnectMQTT(bool autoReconnect) {
    this->autoReconnectMQTT = autoReconnect;
}

void XYXKC_IOT::configureMQTT(String mqtt_user, String mqtt_password) {
    this->mqtt_user = mqtt_user;
    this->mqtt_password = mqtt_password;

    client.setServer(mqtt_server.c_str(), mqtt_port);
    client.setCallback(mqttCallback);
}

static void runOnCore0(void* parameter) {


    while (true) {

        if(!mqttIot->isMQTTConnected()){
            if(mqttIot->isWiFiConnected() && mqttIot->autoReconnectMQTT){
                mqttIot->reconnectMQTT();
            }
            delay(20);
            yield();
        }

        client.loop();
        delay(20);
        yield();
    }
}

void XYXKC_IOT::connectMQTT() {

    if (isWiFiConnected() && !isMQTTConnected() && client.connect("", this->mqtt_user.c_str(), this->mqtt_password.c_str())) {
        if (customTopicnum < CUSTOMTOPICNUMBER + 1 ){
            for(int i = 0; i < mqtt_customTopicArray.size(); i++){
                    client.subscribe(mqtt_customTopicArray[i].c_str());
            }   
        }
    }


    if(!isTheTaskExists){
        isTheTaskExists = true;
        xTaskCreatePinnedToCore(
            runOnCore0,   
            "Task0",      
            4096,
            this,     
            1,            
            NULL,
            XPORTGETCOREID
        );
    }
}  
void XYXKC_IOT::mqttDisconnect(){
    client.disconnect();
}
void XYXKC_IOT::wifiDisconnect(){
    if(client.connected())
        client.disconnect();
    WiFi.disconnect(true);
}


void XYXKC_IOT::publishMessage(const String topic, const String payload, int qos) {
    if(!isWiFiConnected() || !isMQTTConnected() || mqttCallbackBusy)
        return;
    client.setPublishQos(qos > 0 ? MQTTQOS1: MQTTQOS0);
    client.publish(topic.c_str(), payload.c_str());
    delay(250);
}

void XYXKC_IOT::publishMessage(const int topicNumber, const String payload, int qos) {
    if(!isWiFiConnected() || !isMQTTConnected() || mqttCallbackBusy)
        return;
    client.setPublishQos(qos > 0 ? MQTTQOS1: MQTTQOS0);
    client.publish(mqtt_customTopicArray[topicNumber].c_str(), payload.c_str());
    delay(250);
}




void XYXKC_IOT::subscribeTopic(const String topic) {

    if (customTopicnum < CUSTOMTOPICNUMBER + 1){
        for(int i = 0; i < mqtt_customTopicArray.size(); i++){
            if(topic == mqtt_customTopicArray[i]) {
                client.subscribe(topic.c_str());
                return;
            }
            
        }
        if(customTopicnum == CUSTOMTOPICNUMBER) return;
        mqtt_customTopicArray.push_back(topic);
        client.subscribe(topic.c_str());
        customTopicnum = customTopicnum + 1;
    }

}

bool XYXKC_IOT::isWiFiConnected() {
    return WiFi.status() == WL_CONNECTED;
}

bool XYXKC_IOT::isMQTTConnected() {
    return client.state()==0 && client.connected();
}

int XYXKC_IOT::mqttState() {

    return client.state();
}
String XYXKC_IOT::getWiFiIP() {
    if (apMode) {
        return WiFi.softAPIP().toString();
    } else {
        return WiFi.localIP().toString();
    }
}



void XYXKC_IOT::reconnectWIFI() {

    if(!isWiFiConnected()){
        unsigned long time = millis();
        if(time - wifiTime > 12000 || wifiTime == 0){
            wifiTime = time;
            wifiDisconnect();
            WiFi.begin(ssid.c_str(), password.c_str());
        }

    }
    
}



void XYXKC_IOT::reconnectMQTT() {
    if(!isMQTTConnected()){
        unsigned long time = millis();
        if(time - mqttTime > 5000 || mqttTime == 0){
            mqttTime = time;
            if (client.connect("", this->mqtt_user.c_str(), this->mqtt_password.c_str())) {
                if (customTopicnum < CUSTOMTOPICNUMBER + 1){
                    for(int i = 0; i < mqtt_customTopicArray.size(); i++){
                            client.subscribe(mqtt_customTopicArray[i].c_str());
                    }   
                }
            }
        }
    }

}


void XYXKC_IOT::setCustomMqttCallback(const MsgHandleCb handles, const String topic){
    if (customTopicnum < CUSTOMTOPICNUMBER + 1){
        for(int i = 0; i < mqtt_customTopicArray.size(); i++){
            if(topic == mqtt_customTopicArray[i]) {
                mqtt_customMsgHandleCb[i] = handles;
                return;
            }
        }
        if(customTopicnum == CUSTOMTOPICNUMBER) return;
        mqtt_customTopicArray.push_back(topic);
        mqtt_customMsgHandleCb[customTopicnum] = handles;
        customTopicnum = customTopicnum + 1;
    }
    
}
void XYXKC_IOT::setCustomMqttCallback(const String topic, const String delimiters, const HandleCb handles){
    if (customTopicnum < CUSTOMTOPICNUMBER + 1){
        for(int i = 0; i < mqtt_customTopicArray.size(); i++){
            if(topic == mqtt_customTopicArray[i]) {
                mqtt_customHandleCb[i].push_back(handles);
                mqtt_customDelimiters[i].push_back(delimiters);
                return;
            }
        }
        if(customTopicnum == CUSTOMTOPICNUMBER) return;
        mqtt_customTopicArray.push_back(topic);
        mqtt_customHandleCb[customTopicnum].push_back(handles);
        mqtt_customDelimiters[customTopicnum].push_back(delimiters);
        customTopicnum = customTopicnum + 1;
    }

}

void XYXKC_IOT::setCustomMqttCallback(const MsgHandleCb handles, const int topicNumber){
    if (topicNumber < CUSTOMTOPICNUMBER + 1){
        mqtt_customMsgHandleCb[topicNumber] = handles;
    }

}

void XYXKC_IOT::setCustomMqttCallback(const int topicNumber, const String delimiters, const HandleCb handles){
    if (topicNumber < CUSTOMTOPICNUMBER + 1){
        mqtt_customHandleCb[topicNumber].push_back(handles);
        mqtt_customDelimiters[topicNumber].push_back(delimiters);
    }

}
